home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / oobpls10.zip / OLGIF.PAS < prev    next >
Pascal/Delphi Source File  |  1992-11-06  |  14KB  |  474 lines

  1. {$F+,O+,R-,S-,V-,A+}
  2. unit OLGIF;  {online GIF decoder using OOBPLUS services}
  3.  
  4. {$I OPDEFINE.INC}
  5. {.$DEFINE Debug}
  6.  
  7. interface
  8.  
  9. uses
  10.   DOS,
  11.   OpRoot,
  12.   OpInline,
  13.   OpCrt,
  14.   OpMouse,
  15.   OpDrag,
  16.   OpFrame,
  17.   OpWindow,
  18.   ApMisc,
  19.   ApTimer,
  20.   ApPort,
  21.   OOCom,
  22.   OOBPlus,
  23.   DeGIF,
  24.   GIFVideo;
  25.  
  26. const
  27.   UnitVers = '1.0d';
  28.   UnitDate = '05-Jun-91';
  29.   TmpGifName = '$$TEMP$$.GIF';
  30.  
  31. const
  32.   GifCapOK : Boolean = True;
  33.   GifCapName : PathStr = '';
  34.  
  35. function DisplayGIFOnline(APP : AbstractPortPtr;
  36.                           WaitForKey : Boolean) : Boolean;
  37.   {-decodes BPlus-encapsulated GIF image data stream}
  38.  
  39. implementation
  40.  
  41. const
  42.   BuffSize = 2048;                               {size of our local buffer}
  43.   YInc : Array[1..6] of Byte = (8,8,4,2,1,0);    {used for interlaced image}
  44.   YLin : Array[1..6] of Byte = (0,4,2,1,0,0);    {decoding/management}
  45.   YInt : Array[1..6] of Byte = (7,3,1,0,0,0);
  46.  
  47. type
  48.   BuffType = Array[1..$FFF1] of Byte;            {local decode buffer types}
  49.   BuffPtr  = ^BuffType;
  50.  
  51. var
  52.   GBP : BPProtoGIFPtr;       {our GIF BPlus handler}
  53.  
  54. var
  55.   GIFBuff  : BuffPtr;           {our decode I/O buffer}
  56.   GRec     : JumpRecord;        {used for error handling}
  57.   Pass     : Byte;              {interlace pass counter}
  58.   Intrlace : Boolean;           {true if an interlaced image}
  59.   Image    : Word;              {counter for images in this stream}
  60.   Done     : Boolean;           {true when complete}
  61.   GIFCap   : Boolean;           {true if capturing stream to file}
  62.   InBPlus  : Boolean;           {true once B+ processing active}
  63.   BufIdx   : Word;              {current index in the I/O buffer}
  64.   Count    : Word;              {bytes currently in I/O buffer}
  65.   GF       : File;              {file to write stream to}
  66.   EOFin    : Boolean;           {true if we've seen EOF mark in stream}
  67.   SW       : StackWindowPtr;    {used to save underlying screen}
  68.   MouseB   : Boolean;
  69.  
  70. {-------------------------------}
  71. { High-level online GIF decoder }
  72. {-------------------------------}
  73.  
  74.   procedure RingBell;
  75.     {-noisemaker}
  76.   begin
  77.     Sound(440);
  78.     Delay(100);
  79.     NoSound;
  80.   end;
  81.  
  82.   procedure Purge(GBP : BPProtoGIFPtr);
  83.     {-purge pending <DLE> after abort}
  84.   var
  85.     E : EventTimer;
  86.     I : Integer;
  87.     C : Char;
  88.   begin
  89.     with GBP^, APort^ do begin
  90.       for I := 1 to 3 do begin
  91.         NewTimerSecs(E,5);
  92.         while not CharReady do
  93.           if TimerExpired(E) then exit;
  94.         PeekChar(C,1);
  95.         if C <> cDLE then
  96.           exit
  97.         else
  98.           if bpGetGIFDataBlock(GIFBuff^,Count,EOFin) then ;
  99.       end;
  100.     end;
  101.   end;
  102.  
  103.   procedure EndIt(GBP : BPProtoGIFPtr; B : Boolean);
  104.     {-abort processing procedure}
  105.   begin
  106.     if InBPlus then with GBP^ do begin
  107.       if NOT Aborting then
  108.         SendFailure('AAborted by user');
  109.       Purge(GBP);
  110.     end;
  111.     if GraphOn then
  112.       SetTextMode;
  113.     if B then begin
  114.       RingBell;
  115.       RingBell;
  116.     end;
  117.     LongJump(GRec,1);
  118.   end;
  119.  
  120.   function MyGetByte : Byte;
  121.     {-get next byte in stream}
  122.   var B : Boolean;
  123.   begin
  124.     with GBP^ do begin
  125.         {if we've exhausted the last block, read a new one}
  126.       if BufIdx > Count then begin
  127.         if bpGetGIFDataBlock(GIFBuff^,Count,EOFin) then begin
  128. {$IFDEF Debug}
  129.           if NOT GraphOn then
  130.             WriteLn('Packet size=',Count);
  131. {$ENDIF}
  132.           if GIFCap then begin                  {write the file}
  133.             BlockWrite(GF,GIFBuff^,Count);
  134.             if IOResult <> 0 then begin         {whoops!  clean house}
  135.               Close(GF);  if IOResult = 0 then ;
  136.               GIFCap := False;                  {and set our flag}
  137.             end;
  138.           end;
  139.           bpSendACK;                            {acknowledge the packet}
  140.           BufIdx := 1;                          {reset the buffer index}
  141.         end
  142.         else begin                              {failed packet read, abort...}
  143. {$IFDEF Debug}
  144.           if NOT GraphOn then begin
  145.             WriteLn('Unable to read B+ data packet - Aborting...');
  146.             Delay(2000);
  147.           end;
  148. {$ENDIF}
  149.           EndIt(GBP,True);                      {and leave}
  150.         end;
  151.       end;
  152.     end;
  153.  
  154.     MyGetByte := GIFBuff^[BufIdx];              {get the byte}
  155.     Inc(BufIdx);                                {keep counter straight}
  156.   end;
  157.  
  158.   procedure MyPutLine;
  159.     {-plot the raster line of pixels to hardware, handle interlacing}
  160.   var I : Integer;
  161.   begin
  162.     if YCord <= Raster then          {don't wrap back to top of screen!}
  163.       PlotLine(YCord);
  164.     Inc(YCord,YInc[Pass]);           {select next line to plot per interlace}
  165.     if YCord >= BotEdge then begin
  166.       if Pass < 5 then Inc(Pass);    {reset to top of image per interlace}
  167.       YCord := YLin[Pass] + TopEdge;
  168.     end;
  169.   end;
  170.  
  171.   procedure MyPutLineDbl;
  172.     {-our decoder's PutLine proc.  This method accomodates interlaced GIFs}
  173.   var I : Integer;
  174.   begin
  175.     if YCord <= Raster then        {don't wrap back to top of screen!}
  176.       PlotLine(YCord);
  177.     Inc(YCord,YInc[Pass] shl 1);
  178.     if YCord >= BotEdge then begin
  179.       if Pass < 5 then Inc(Pass);
  180.       YCord := (YLin[Pass] shl 1) + TopEdge;
  181.     end;
  182.   end;
  183.  
  184.   procedure AdjustVars;
  185.     {-match decode/display vars to image sizes}
  186.   var I : Byte;
  187.   begin
  188.     Inc(Image);
  189.     Pass := 5;
  190.     IntrLace := FALSE;
  191.     LeftEdge  := ImageLeft;
  192.     TopEdge   := ImageTop;
  193.     if (ScrWidth = 300) and (ScrHeight = 200) then begin
  194.       Inc(LeftEdge, 10);
  195.       RightEdge := ImageWidth + LeftEdge;
  196.       BotEdge   := ImageHeight + TopEdge;
  197.     end
  198.     else if (ScrWidth = 378) and (ScrHeight = 240) then begin
  199.       if (DoDbl) then begin
  200.         RightEdge := 700;
  201.         BotEdge := 480;
  202.       end
  203.       else begin
  204.         Inc(LeftEdge, 131);
  205.         Inc(TopEdge, (Raster shr 1) - 120);
  206.         RightEdge := ImageWidth + LeftEdge;
  207.         BotEdge   := ImageHeight + TopEdge;
  208.       end;
  209.     end
  210.     else begin
  211.       if ImageWidth < Pixels then
  212.         Inc(LeftEdge, (Pixels shr 1) - (ImageWidth shr 1));
  213.       if ImageHeight < Raster then
  214.         Inc(TopEdge, (Raster shr 1) - (ImageHeight shr 1));
  215.       RightEdge := ImageWidth + LeftEdge;
  216.       BotEdge   := ImageHeight + TopEdge;
  217.     end;
  218.     YCord := TopEdge;
  219.     if Maps[Local].Interlaced then
  220.       Pass := 1;
  221.   end;
  222.  
  223.   function OnLineGIFSig : Boolean;
  224.     {-init B+ proto for GIF and get signature.  The scenario is:
  225.  
  226.          (host->remote)  <ENQ>
  227.          (host<-remote)  <DLE>++<DLE>0
  228.          (host->remote)  BPlus "+" packet
  229.          (host<-remote)  process "+" packet, send ACK packet
  230.          (host->remote)  first "N" packet containing actual GIF stream...
  231.  
  232.     For hysterical, uh, historical reasons we wait up to 6 chars to receive
  233.     the handshake for the protocol.  (Actually, until recently there were a few
  234.     areas of CIS, such as TREND, that did not provide B+ encapsulation and just
  235.     sent the stream; we had to be able to get either a handshake or the GIF
  236.     signature, and if no B+ then abandon proto processing and get the stream
  237.     "raw".) }
  238.  
  239.   var C : Char;
  240.       S : String[5];
  241.       I,X : Integer;
  242.   begin
  243.       {set things up}
  244.     OnlineGIFSig := False;
  245.     I := 0;
  246.     GIFSig := '';
  247. {$IFDEF Debug}
  248.     WriteLn('Getting GIF signature...');
  249. {$ENDIF}
  250.  
  251.       {loop getting bytes from the port and processing}
  252.     repeat
  253.       Inc(I);
  254.       C := #0;
  255.       AsyncStatus := ecOK;
  256.       if I = 1 then X := 30 else X := 10;      {30 secs for first byte, else 10}
  257.       GBP^.APort^.GetCharTimeOut(C,Secs2Tics(X));
  258.       if AsyncStatus <> ecOK then              {read failed, drop out}
  259.         Exit;
  260.  
  261.       case C of
  262.         #5 :   {<ENQ> seen, respond}
  263.           begin
  264.             GBP^.bpHandleENQ;
  265.             Dec(I);  {dec counter to allow more chars}
  266.           end;
  267.         #16:   {<DLE> starting "+" packet seen, handle it}
  268.           begin
  269.             if GBP^.bpDLESeen then begin    {"+" packet OK, we outa here:}
  270.               OnlineGIFSig := True;
  271.               InBPlus := True;
  272.               GetGIFSig;                    {force first packet read, get}
  273.               exit;                         {6-byte signature for check}
  274.             end
  275.             else
  276.               exit;                         {"+" packet failed, get out}
  277.           end;
  278.         else
  279.           GIFSig := GIFSig + C;             {attempt build of "raw" signature}
  280.       end;
  281.     until I >= 6;
  282.     OnlineGIFSig := True;
  283.   end;
  284.  
  285.   function PortQuiese(AP : AbstractPortPtr; MinWait,MaxWait : Word) : Boolean;
  286.     {-wait at least MinWait secs for port "quiet", up to MaxWait secs}
  287.   var
  288.     E1,E2 : EventTimer;
  289.     Tmp : BPtr;
  290.   begin
  291.     PortQuiese := True;
  292.     with AP^.Pr^ do begin
  293.       NewTimer(E1,Secs2Tics(MaxWait));
  294.       repeat
  295.         Tmp := InHead;
  296.         NewTimer(E2,Secs2Tics(MinWait));
  297.         while not TimerExpired(E2) do ;
  298.         if Tmp = InHead then exit;
  299.       until TimerExpired(E1);
  300.       PortQuiese := False;
  301.     end;
  302.   end;
  303.  
  304.   function DecodeGIF(GBP : BPProtoGIFPtr) : Integer;
  305.     {-GIF stream decode logic}
  306.   var I         : Integer;
  307.       BlockType : Char;
  308.   begin
  309.       {init vars}
  310.     Done := False;
  311.     Image := 0;
  312.     CurMap := Global;
  313.     DecodeGIF := -9;
  314.  
  315.       {get signature (inits BPlus protocol)}
  316.     if NOT OnlineGIFSig then
  317.       EndIt(GBP,False);
  318.  
  319.       {verify signature.  To accomodate future versions, we accept anything}
  320.       {with the first 3 chars "GIF" and the next 3 as 2 digits and a lower }
  321.       {case char.                                                          }
  322.     if (Pos('GIF',GIFSig) <> 1) or
  323.        (NOT(GIFSig[4] in ['0'..'9'])) or
  324.        (NOT(GIFSig[5] in ['0'..'9'])) or
  325.        (NOT(GIFSig[6] in ['a'..'z'])) then
  326.       EndIt(GBP,True);
  327.  
  328.       {get the hardware specifics, match a video mode as close as we can}
  329.     GetScrDes(Maps[Global]);
  330.     SelMode := SelectMode(ScrWidth,ScrHeight);
  331.     if SelMode = 0 then EndIt(GBP,True);
  332.  
  333.       {if we have a global map, process it}
  334.     if Maps[Global].MapExists then
  335.       DoMapping
  336.     else
  337.       SetDefMap;
  338.  
  339.       {kick into graphics mode then juggle the palette to match our map}
  340.  
  341.     with GBP^, APort^ do begin
  342.       PutChar(cXoff);                     {tell host to stop transmitting}
  343.       if PortQuiese(APort,1,6) then ;     {wait for port to quiese}
  344.       HideMousePrim(MouseB);              {hide the mouse}
  345.       SW^.Draw;                           {save the screen}
  346.       if (CurrentDisplay in [EGA,VGA]) and
  347.          (ScrWidth = 378) and
  348.          (ScrHeight = 240) then
  349.         if (DoDbl) then
  350.           PutLine := MyPutLineDbl;
  351.       SetGraphicsMode(SelMode);           {set graphics mode}
  352.       AdjustPalette(SelMode);             {and juggle the palette}
  353.       PutChar(cXon);                      {tell host it can start again}
  354.     end;
  355.  
  356.       {loop reading blocks and processing...}
  357.     while NOT Done do begin
  358.       BlockType := Chr(GetByte);  {get blocktype char}
  359.       case BlockType of
  360.         ',': begin         {"Local descriptor"/image, process...}
  361.                GetImageDescription(Maps[Local]);
  362.                AdjustVars;
  363.                if Maps[Local].MapExists then begin
  364.                    {juggle palette again}
  365.                  CurMap := Local;
  366.                  DoMapping;
  367.                  AdjustPalette(SelMode);
  368.                end;
  369.                  {decode the image data and display}
  370.                I := ExpandGIF;
  371.                if I <> 0 then begin
  372.                  DecodeGIF := I;          {decoder error (LZW couldn't decomp)}
  373.                  EndIt(GBP,True);
  374.                end;
  375.                CurMap := Global;  {reselect global map for possible next image}
  376.              end;
  377.         '!': SkipExtendBlock;     {"Extension" block, we discard}
  378.         ';': begin                {Terminator seen, clean up and go home}
  379.                Done := True;
  380.                  {a "TC" packet will be pending, get it}
  381.                with GBP^ do while NOT EOFin do
  382.                  if bpGetGIFDataBlock(GIFBuff^,Count,EOFin) then
  383.                    bpSendACK;
  384.                InBPlus := False;
  385.                  {if the capture file is open, close it}
  386.                if GIFCap then begin
  387.                  Close(GF);  if IOResult = 0 then ;
  388.                  GifCapOK := True;
  389.                end;
  390.                DecodeGIF := 0;
  391.                exit;
  392.              end;
  393.       end;
  394.     end;
  395.   end;
  396.  
  397.   function DisplayGIFOnLine(APP : AbstractPortPtr;
  398.                             WaitForKey : Boolean): Boolean;
  399.     {-our high-level online decoder}
  400.   label
  401.     Break;
  402.   var L : LongInt;
  403.       W : Word;
  404.       C : Char Absolute W;
  405.       N : Integer;
  406.       B : Boolean;
  407.   begin
  408.     DisplayGIFOnline := False;
  409.     InBPlus := False;
  410.     MouseB := True;
  411.     GifCapOK := False;
  412.     GBP := nil;
  413.  
  414.     if NOT GetMemCheck(GIFBuff,BuffSize) then
  415.       exit;
  416.  
  417.     New(SW, Init(1, 1, ScreenWidth, ScreenHeight));
  418.     if SW = nil then begin
  419.       FreeMemCheck(GIFBuff, BuffSize);
  420.       exit;
  421.     end;
  422.  
  423.       {init protocol object}
  424.     New(GBP,Init(APP));
  425.     if GBP = NIL then
  426.       goto Break;
  427.  
  428.       {point to our get/put routines}
  429.     GetByte := MyGetByte;
  430.     PutLine := MyPutLine;
  431.  
  432.       {init error handler}
  433.     N := SetJump(GRec);
  434.     if N <> 0 then
  435.       goto Break;
  436.  
  437.       {set buffer vars to force initial read}
  438.     Count := 0;
  439.     BufIdx := 999;
  440.  
  441.       {init capture file}
  442.     Assign(GF, TmpGifName);
  443.     Rewrite(GF, 1);
  444.     GIFCap := (IOResult = 0);
  445.  
  446.       {process...}
  447.     N := DecodeGIF(GBP);
  448.  
  449.       {if successful, wait for keypress}
  450.     if N = 0 then begin
  451.       RingBell;
  452.       DisplayGIFOnline := GIFCap;
  453.         {wait for <CR> or <ESC> before clearing}
  454.       if WaitForKey then repeat
  455.         W := ReadKeyOrButton;
  456.       until (C = #13) or (C = #27) or (Hi(W) in [$ED, $EE, $EF]);
  457.       ClearMouseEvents;
  458.     end;
  459.     SetTextMode;
  460.  
  461. Break:
  462.     if GBP <> nil then
  463.       Dispose(GBP, Done);
  464.     if SW^.IsActive then
  465.       SW^.EraseHidden;
  466.  
  467.     MouseGoToXY(ScreenWidth shr 1, ScreenHeight shr 1);
  468.     ShowMousePrim(MouseB);
  469.     Dispose(SW, Done);
  470.     FreeMemCheck(GIFBuff, BuffSize);
  471.   end;
  472.  
  473. end.
  474.